package org.lemon.transport.context.adaptor;

import com.google.gson.Gson;
import com.google.gson.JsonParser;
import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import lombok.extern.slf4j.Slf4j;
import org.lemon.transport.context.utils.StringUtils;
import org.server.transport.gen.TransportProto.*;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class ProtoConverter {

    public static final Gson GSON = new Gson();
    public static final JsonParser JSON_PARSER = new JsonParser();

    public static PostTelemetryMsg convertToTelemetry(byte[] payload) throws InvalidProtocolBufferException {

        TsKeyValueListProto protoPayload = TsKeyValueListProto.parseFrom(payload);
        TsKeyValueListProto tsKvListProto = validateTsKvListProto(protoPayload);
        PostTelemetryMsg.Builder builder = PostTelemetryMsg.newBuilder();
        builder.addTsKvList(tsKvListProto);
        return builder.build();
    }

    public static ProvisionDeviceRequestMsg convertToProvisionRequestMsg(byte[] bytes) throws InvalidProtocolBufferException {
        return ProvisionDeviceRequestMsg.parseFrom(bytes);
    }

    private static TsKeyValueListProto validateTsKvListProto(TsKeyValueListProto kvListProto) {

        kvListProto.getKvList().forEach(keyValueProto -> {
            String key = keyValueProto.getKey();
            if (StringUtils.isNullOrEmpty(key)) {
                throw new IllegalArgumentException("Invalid key value: " + key + "!");
            }
            KeyValueType type = keyValueProto.getType();
            switch (type) {
                case BOOLEAN_V:
                case LONG_V:
                case DOUBLE_V:
                    break;
                case STRING_V:
                    if (StringUtils.isNullOrEmpty(keyValueProto.getStringV())) {
                        throw new IllegalArgumentException("Value is empty for key: " + key + "!");
                    }
                case JSON_V:
                    try {
                        JSON_PARSER.parse(keyValueProto.getJsonV());
                    } catch (Exception e) {
                        throw new IllegalArgumentException("Can't parse value: " + keyValueProto.getJsonV() + " for key: " + key + "!");
                    }
                case UNRECOGNIZED:
                    throw new IllegalArgumentException("Unsupported keyValueType: " + type + "!");
            }
        });
        if (kvListProto.getTs() == 0) {
            TsKeyValueListProto.Builder builder = TsKeyValueListProto.newBuilder();
            builder.setTs(System.currentTimeMillis());
            builder.addAllKv(kvListProto.getKvList());
            return builder.build();
        }
        return kvListProto;
    }

    private static List<KeyValueProto> validateKeyValueProto(List<KeyValueProto> kvList) {
        kvList.forEach(keyValueProto -> {
            String key = keyValueProto.getKey();
            if (StringUtils.isNullOrEmpty(key)) {
                throw new IllegalArgumentException("Invalid key value: " + key + "!");
            }
            KeyValueType type = keyValueProto.getType();
            switch (type) {
                case BOOLEAN_V:
                case LONG_V:
                case DOUBLE_V:
                    break;
                case STRING_V:
                    if (StringUtils.isNullOrEmpty(keyValueProto.getStringV())) {
                        throw new IllegalArgumentException("Value is empty for key: " + key + "!");
                    }
                    break;
                case JSON_V:
                    try {
                        JSON_PARSER.parse(keyValueProto.getJsonV());
                    } catch (Exception e) {
                        throw new IllegalArgumentException("Can't parse value: " + keyValueProto.getJsonV() + " for key: " + key + "!");
                    }
                    break;
                case UNRECOGNIZED:
                    throw new IllegalArgumentException("Unsupported keyValueType: " + type + "!");
            }
        });
        return kvList;
    }

    public static Descriptors.Descriptor validateDescriptor(Descriptors.Descriptor descriptor) throws AdaptorException {
        if (descriptor == null) {
            throw new AdaptorException("Failed to get dynamic message descriptor!");
        }
        return descriptor;
    }

    public static PostTelemetryMsg validatePostTelemetryMsg(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException {
        PostTelemetryMsg msg = PostTelemetryMsg.parseFrom(payload);
        PostTelemetryMsg.Builder postTelemetryMsgBuilder = PostTelemetryMsg.newBuilder();
        List<TsKeyValueListProto> tsKvListList = msg.getTsKvListList();
        if (CollectionUtils.isEmpty(tsKvListList)){
            throw new IllegalArgumentException("TsKv list is empty!");
        }
        List<TsKeyValueListProto> tsKvProtoList = new ArrayList<>();
        tsKvListList.forEach(tsKvListProto -> {
            TsKeyValueListProto transportTsKvListProto = validateTsKvListProto(tsKvListProto);
            tsKvProtoList.add(transportTsKvListProto);
        });
        postTelemetryMsgBuilder.addAllTsKvList(tsKvProtoList);
        return postTelemetryMsgBuilder.build();
    }

    public static PostAttributeMsg validatePostAttributesMsg(byte[] payload) throws InvalidProtocolBufferException, IllegalArgumentException {
        PostAttributeMsg msg = PostAttributeMsg.parseFrom(payload);
        List<KeyValueProto> kvList = msg.getKvList();
        if (CollectionUtils.isEmpty(kvList)){
            throw new IllegalArgumentException("KeyValue list is empty");
        }
        List<KeyValueProto> keyValueProtoList = validateKeyValueProto(kvList);
        PostAttributeMsg.Builder result = PostAttributeMsg.newBuilder();
        result.addAllKv(keyValueProtoList);
        return result.build();
    }

    public static String dynamicMsgToJson(byte[] bytes, Descriptors.Descriptor descriptor) throws InvalidProtocolBufferException {
        DynamicMessage dynamicMessage = DynamicMessage.parseFrom(descriptor, bytes);
        return JsonFormat.printer().includingDefaultValueFields().print(dynamicMessage);
    }
}
